package com.yong.common.hibernate.impl; 

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.stereotype.Repository;
import org.springframework.util.Assert;

import com.epcool.framework.dao.IGenericDao;
import com.epcool.framework.util.Paging;


/**
 * Description: 泛型hibernate Dao实现基类
 * @author:     yz.Wu
 * @param: 		<T> a type variable
 * @param: 		<PK> the primary key for that type 
 */
@Repository
public class GenericDaoHibernate<T, PK extends Serializable> implements IGenericDao<T, PK> {
	
	protected transient final Log log = LogFactory.getLog(getClass());
	
	@Autowired
	protected SessionFactory sessionFactory;
	
	public Session getSession() {
		return sessionFactory.getCurrentSession();
	}
	
	private Class<T> persistentClass;//实体对象
	private String className;//实体类名
	private static final Object[] EMPTY = {};//空对象组
	
	@SuppressWarnings("unchecked")
	public GenericDaoHibernate() {
		this.persistentClass = (Class<T>) ((ParameterizedType) getClass()
				.getGenericSuperclass()).getActualTypeArguments()[0];
		this.className = persistentClass.getName();
	}
	
	public GenericDaoHibernate(final Class<T> persistentClass) {
        this.persistentClass = persistentClass;
        this.className = persistentClass.getName();
    }
	
    /**
	 * 创建Query对象.
	 * @param values 可变参数.
	 */
	protected Query createQuery(String queryString, Object... values) {
		Assert.hasText(queryString);
		Query query = getSession().createQuery(queryString);
		if (values != null && values.length > 0) {
			for (int i = 0; i < values.length; i++) {
				query.setParameter(i, values[i]);
			}
		}
		return query;
	}
	
	@SuppressWarnings("unchecked")
	protected Query createStrongQuery(String queryString, Object... values) {
		Assert.hasText(queryString);
		Query q = getSession().createQuery(queryString);
		if (values == null) {
			return q;
		}
		
		for (int i = 0; i < values.length; i++) {
			if (values[i].getClass().isArray()) {
				Object[] o = (Object[]) values[i];
				if (o[1] instanceof java.util.Date) {
					q.setDate(o[0].toString(), (java.util.Date) o[1]);
				}
				if (o[1] instanceof java.sql.Date) {
					q.setDate(o[0].toString(), (java.sql.Date) o[1]);
				} else if (o[1] instanceof java.sql.Timestamp) {
					q.setTimestamp(o[0].toString(),
							(java.sql.Timestamp) o[1]);
				} else if (o[1] instanceof java.lang.Integer) {
					q.setInteger(o[0].toString(), new Integer(o[1]
							.toString()).intValue());
				} else if (o[1] instanceof java.util.ArrayList) {
					q.setParameterList(o[0].toString(),
							(java.util.ArrayList) o[1]);
				}
			} else {
				if (values[i] instanceof java.sql.Timestamp) {
					q.setTimestamp(i,
							(java.sql.Timestamp) values[i]);
				} else {
					q.setParameter(i, values[i]);
				}

			}
		}
		return q;
	}

    /**
     * {@inheritDoc}
     */
    @SuppressWarnings("unchecked")
    public boolean exists(PK id) {
    	T entity = (T) getSession().get(this.persistentClass, id);
        return entity != null;
	}

    /** 
     * {@inheritDoc}
     */
    @SuppressWarnings("unchecked")
    public List<T> find(String queryString, int startIndex, int pageSize,
			Object... values) {
    	Query query = createQuery(queryString, values);
    	List<T> list = null;
    	if (pageSize == 0) {
    		list = query.list();
    	} else {
    		list = query.setFirstResult(startIndex).setMaxResults(pageSize).list();
    	}
    	
		return list;
	}
    
    public List<?> findNotT(String queryString, int startIndex, int pageSize,
			Object... values) {
    	Query query = createQuery(queryString, values);
    	List<?> list = null;
    	if (pageSize == 0) {
    		list = query.list();
    	} else {
    		list = query.setFirstResult(startIndex).setMaxResults(pageSize).list();
    	}
		return list;
	}
    
    /** 
     * {@inheritDoc}
     */
    public int getCount(String queryString) {
    	Object[] o = {};
    	return getCountByProperties(queryString, o);
    }
    
    /** 
     * {@inheritDoc}
     */
    public int getCountByProperties(String queryString, Object... values) {
		try {
			String countQueryString = " select count(*) "
					+ removeSelect(removeOrders(queryString));
			Query query = createQuery(countQueryString, values);
			return ((Long) (query.iterate().next())).intValue();
		} catch (HibernateException e) {
			e.printStackTrace();
			return 0;
		}
	}

    /** 
     * {@inheritDoc}
     */
    @SuppressWarnings("unchecked")	
    public List<T> findAll(int startIndex, int pageSize) {
		if (pageSize == 0) {			
			return find("from " + this.className, 0, 0, EMPTY);
		} else {	
			return find("from " + this.className, startIndex, pageSize, EMPTY);
		}
	}

    /** 
     * {@inheritDoc}
     */
    @SuppressWarnings("unchecked")
    public List<T> findByProperty(String propertyName, Object value,
			int startIndex, int pageSize) {
    	String queryString = "select model from " + className + " model where model." + propertyName + "= ?";
    	Object[] o = {value};
		return find(queryString, startIndex, pageSize, o);
	}

    public T findUniqueByProperty(String propertyName, Object value) {
		return uniqueResult(findByProperty(propertyName, value, 0, 0));
	}

	public T findUniqueByProperties(String queryString, Object... values) {
		return uniqueResult(find(queryString, 0, 0, values));
	}
	    
	/** 
     * {@inheritDoc}
     */
    @SuppressWarnings("unchecked")
	public T get(PK id) {
    	T entity = (T) getSession().get(this.persistentClass, id);

        if (entity == null) {
            log.warn(this.persistentClass + "' object with id '" + id + "' not found...");
            //throw new ObjectRetrievalFailureException(this.persistentClass, id);
        }

        return entity;
	}
    
    /** 
     * {@inheritDoc}
     */
    @SuppressWarnings("unchecked")
	public T load(PK id) {
    	T entity = (T) getSession().load(this.persistentClass, id);

        if (entity == null) {
            log.warn(this.persistentClass + "' object with id '" + id + "' not found...");
            //throw new ObjectRetrievalFailureException(this.persistentClass, id);
        }

        return entity;
	}

    /** 
     * {@inheritDoc}
     */
    public void remove(PK id) {
    	getSession().delete(this.get(id));
    	getSession().flush();
	}
    
    public void remove(T t) {
    	getSession().delete(t);
    	getSession().flush();
    }

	/** 
     * {@inheritDoc}
     */
    @SuppressWarnings("unchecked")
    public T save(Object object) {
		return (T) getSession().merge(object);
	}    

	/**
	 * 去除hql的select 子句，未考虑union的情况,用于pagedQuery.
	 * @param queryString
	 * @return
	 */
    private static String removeSelect(String queryString) {
		int beginPos = queryString.toLowerCase().indexOf("from");
		return queryString.substring(beginPos);
	}
	
	/**
	 * 去除hql的orderby 子句，用于pagedQuery.
	 * @param queryString
	 * @return
	 */
    private static String removeOrders(String queryString) {
		Pattern p = Pattern.compile("order\\s*by[\\w|\\W|\\s|\\S]*", Pattern.CASE_INSENSITIVE);
		Matcher m = p.matcher(queryString);
		StringBuffer sb = new StringBuffer();
		while (m.find()) {
			m.appendReplacement(sb, "");
		}
		m.appendTail(sb);
		return sb.toString();
	}
	
	/**
	 * 取出列表的第一个对象.
	 * 如果列表为空返回Null,如果有多于一个对象,抛出异常.
	 */
	private static <E> E uniqueResult(Collection<E> results) {
		if (results == null || results.isEmpty())
			return null;
		if (results.size() > 1)
			throw new IllegalArgumentException("the Collection size is larger than 1");
		return results.iterator().next();
	}

	/** 以下为y.nie 所添加 */
	// 得到Hibernate操作模板
	private HibernateTemplate getHibernateTemplate() {
		return new HibernateTemplate(sessionFactory);
	}

	public int bulkUpdate(String hql, Object... values) {
		return getHibernateTemplate().bulkUpdate(hql, values);
	}

	// 利用已有函数，组装而成
	public Entry<Integer, List<T>> pagedFind(String queryString,
			int startIndex, int pageSize, Object... values) {
		
		int totalCount = 0;
		try {
			totalCount = getCountByProperties(queryString, values);
		} catch (Exception e) {
		}
		
		List<T> list = new java.util.ArrayList<T>();
		try {
			if (totalCount != 0)
				list = find(queryString, startIndex, pageSize, values);
		} catch (Exception e) {
			// 构建一个空的数组
			//Ignore
		}
		
		Map<Integer, List<T>> map = new HashMap<Integer, List<T>>();
		map.put(totalCount, list);
		return map.entrySet().iterator().next();
	}

	public void saveOrUpdate(T entity) {
		getSession().saveOrUpdate(entity);
		getSession().flush();
	}

	public void saveOrUpdateAll(Collection<T> entities) {
		getHibernateTemplate().saveOrUpdateAll(entities);
		getSession().flush();
	}

	public void update(T entity) {
		getSession().update(entity);
		getSession().flush();
	}	
	
	public void mergeUpdate(T entity) {
		getSession().merge(entity);
	}
	
	
	public void paging(Paging<T> paging, String queryString, Object...objects) {
		
		try {
			paging.setTotal(getCountByProperties(queryString, objects));
			
			if (paging.getTotal() != 0) {
				paging.setList(find(queryString, paging.getStart(), 
						paging.getPageSize(), objects));
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}
}
 